package com.easy.orm.jdbc.transactional;

import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 事务管理
 * 
 * @author kun
 *
 * @param <T> 数据库操作对象
 */
public abstract class AbstractTransactionalManager<T extends TransactionalOperationsInterface> {

	/**
	 * 日志
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransactionalManager.class);

	/**
	 * 事务名称
	 */
	private String transactionName;

	/**
	 * 数据源装载对象
	 */
	private Map<String, Object> sources = new Hashtable<>(1);

	/**
	 * 事务操作对象
	 */
	private Hashtable<String, T> operationsTable = new Hashtable<>(1);

	/**
	 * 实例化
	 */
	public AbstractTransactionalManager() {
		super();
	}

	/**
	 * 实例化
	 * 
	 * @param transactionName 事务名称
	 */
	public AbstractTransactionalManager(String transactionName) {
		super();
		operationsTable = new Hashtable<>(1);
		this.transactionName = transactionName;
	}

	/**
	 * 实例化
	 * 
	 * @param sources 数据源获取对象
	 */
	public AbstractTransactionalManager(Map<String, Object> sources) {
		super();
		operationsTable = new Hashtable<>(sources.size());
		this.sources.putAll(sources);
		putAll(sources);
	}

	/**
	 * 实例化
	 * 
	 * @param transactionName 事务名称
	 * @param sources 数据源获取对象
	 */
	public AbstractTransactionalManager(String transactionName, Map<String, Object> sources) {
		super();
		this.transactionName = transactionName;
		operationsTable = new Hashtable<>(sources.size());
		putAll(sources);
	}

	/**
	 * 获取事务名称
	 * 
	 * @return 事务名称
	 */
	public String getTransactionName() {
		return transactionName;
	}

	/**
	 * 
	 * 设置事务名称
	 * 
	 * @param transactionName 事务名称
	 * @return 事务管理器
	 */
	public AbstractTransactionalManager<T> setTransactionName(String transactionName) {
		this.transactionName = transactionName;
		return this;
	}

	/**
	 * 添加索引
	 * 
	 * @param sources 数据来源
	 * @return 事务管理器
	 */
	public AbstractTransactionalManager<T> putAll(Map<String, Object> sources) {
		for (Map.Entry<String, Object> entry : sources.entrySet()) {
			operationsTable.put(entry.getKey(), newOperations(entry.getValue()));
		}
		return this;
	}

	/**
	 * 添加事务 操作
	 * 
	 * @param key key
	 * @param value 数据源获取途径
	 * @return 事务管理器
	 */
	public AbstractTransactionalManager<T> put(String key, Object value) {
		operationsTable.put(key, newOperations(value));
		return this;
	}

	/**
	 * 获取数据库操作对象
	 * 
	 * @param key 对象key
	 * @return 数据操作对象
	 */
	public T getJdbcTemplate(String key) {
		return operationsTable.get(key);
	}

	/**
	 * 事务执行
	 */
	public void transactionalExecute() {
		try {
			execute();
			commit();
		} catch (Exception e) {
			LOGGER.error("[{}] transaction error ! {}", transactionName, e);
			try {
				rollback();
			} catch (SQLException e1) {
				LOGGER.error("[{}] transaction rollback error  ! {}", transactionName, e1);
				throw new RuntimeException(e1);
			}
		} finally {
			try {
				close();
			} catch (SQLException e) {
				LOGGER.error("[{}] transaction close error  ! {}", transactionName, e);
				throw new RuntimeException(e);
			}
		}
	}

	/**
	 * <b>事务提交</b>
	 * <p>
	 * 
	 * @see java.sql.Connection#commit()
	 *      </p>
	 * @throws SQLException 事务提交异常
	 */
	private void commit() throws SQLException {
		LOGGER.info("[{}] transaction commit ! ", transactionName);
		for (Map.Entry<String, ? extends TransactionalOperationsInterface> entry : operationsTable.entrySet()) {
			entry.getValue().commit();
		}
	}

	/**
	 * <b>事务回滚</b>
	 * <p>
	 * 
	 * @see java.sql.Connection#rollback()
	 *      </p>
	 * @throws SQLException 回滚异常
	 */
	private void rollback() throws SQLException {
		LOGGER.info("[{}] transaction rollback ! ", transactionName);
		for (Map.Entry<String, ? extends TransactionalOperationsInterface> entry : operationsTable.entrySet()) {
			entry.getValue().rollback();
			entry.getValue().destroy();
		}
		operationsTable.clear();
	}

	/**
	 * <b>关闭事务</b>
	 * 
	 * <p>
	 * 
	 * @see java.sql.Connection#close()
	 *      </p>
	 * 
	 * @throws SQLException 关闭异常
	 */
	private void close() throws SQLException {
		LOGGER.info("[{}] transaction close ! ", transactionName);
		for (Map.Entry<String, ? extends TransactionalOperationsInterface> entry : operationsTable.entrySet()) {
			entry.getValue().close();
		}
	}

	/**
	 * 实例化操作对象
	 * 
	 * @param source 数据源
	 * @return 操作对象
	 */
	public abstract T newOperations(Object source);

	/**
	 * 执行
	 * 
	 * @throws SQLException 执行异常 {@link java.sql.SQLException}
	 */
	public abstract void execute() throws SQLException;

}
